import cv2
import numpy
import pyautogui
import typing
import win32gui,win32con,win32ui
import time


def __Find_Template_Localtion(_image : numpy.ndarray,_template_image : numpy.ndarray) -> tuple[float,float]:
    """内部函数,传入两个 OpenCV的图像对象,进行一个模板匹配.返回一个位置参数.

    Args:
        _image (_type_): 大图
        _template_image (_type_): 小图

    Returns:
        _type_: tuple(x,y)
    """
    # 执行模板匹配
    result = cv2.matchTemplate(_image, _template_image, cv2.TM_CCOEFF_NORMED)
    # 设置匹配阈值（可以根据需要调整）
    threshold = 0.9
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

    # 如果最大值大于阈值，则认为找到了匹配
    if max_val >= threshold:
        # 确定匹配位置
        top_left = max_loc
        bottom_right = (top_left[0] + _template_image.shape[1], top_left[1] + _template_image.shape[0])
        center_x = top_left[0] + _template_image.shape[1] / 2
        center_y = top_left[1] + _template_image.shape[0] / 2
        return center_x,center_y
    else:
        return None


def __Screenshot() -> numpy.ndarray:
    """对屏幕进行截图操作.

    Returns:
        numpy.ndarray: 屏幕画面的 numpy.ndarray 数据.
    """
    screenshot = pyautogui.screenshot()
    screenshot = cv2.cvtColor(numpy.array(screenshot), cv2.COLOR_RGB2BGR)
    return screenshot


def __Screenshot_By_Window_Handle(window_handle: int) -> typing.Optional[numpy.ndarray]:
    """
    根据窗口句柄截取窗口的截图。

    Args:
        window_handle (int): 要截图的窗口句柄。

    Returns:
        Optional[numpy.ndarray]: 截取的窗口截图，以 numpy 数组形式表示，如果出现错误则返回 None。
    """
    try:
        # 获取窗口的客户区大小
        left, top, right, bottom = win32gui.GetClientRect(window_handle)
        width = (right - left) * 2
        height = (bottom - top) * 2

        # 获取窗口的设备上下文（Device Context, DC）
        hwndDC = win32gui.GetWindowDC(window_handle)
        mfcDC = win32ui.CreateDCFromHandle(hwndDC)
        saveDC = mfcDC.CreateCompatibleDC()

        # 创建一个位图对象
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
        saveDC.SelectObject(saveBitMap)

        # 截图
        saveDC.BitBlt((0, 0), (width, height), mfcDC, (left, top), win32con.SRCCOPY)

        # 将位图对象转换为 numpy 数组
        bmpinfo = saveBitMap.GetInfo()
        bmpstr = saveBitMap.GetBitmapBits(True)
        expected_size = height * width * 4
        if len(bmpstr) == expected_size:
            screenshot = numpy.frombuffer(bmpstr, dtype='uint8')
            screenshot.shape = (height, width, 4)
        else:
            print(f"位图数据长度错误，期望长度为 {expected_size}，实际长度为 {len(bmpstr)}")
            return None

        # 释放资源
        win32gui.DeleteObject(saveBitMap.GetHandle())
        saveDC.DeleteDC()
        mfcDC.DeleteDC()
        win32gui.ReleaseDC(window_handle, hwndDC)

        # 转换为 BGR 格式，去除 Alpha 通道
        screenshot = screenshot[:, :, :3]
        return screenshot
    except Exception as e:
        print(f"发生错误: {e}")
        return None


def _FindPic_From_DC(_template) -> tuple[float,float]:
    """从当前屏幕找匹配模板

    Args:
        _template: 模板图片，可以是图片路径或者 numpy.ndarray 数据
    """
    image = __Screenshot()

    if isinstance(_template, str):
        # 如果传入的是图片路径，读取图片
        template = cv2.imread(_template, cv2.IMREAD_COLOR)
        if template is None:
            print("模板图像无法读取，请检查路径。")
            return None
    elif isinstance(_template, numpy.ndarray):
        # 如果传入的是 numpy.ndarray 数据，直接使用
        template = _template
    else:
        print("模板参数类型错误，请传入图片路径或 numpy.ndarray 数据。")
        return None

    return __Find_Template_Localtion(image, template)


def _FindPic_From_Handle(window_handle: int, _template) -> tuple[float,float]:
    """
    从指定窗口句柄对应的窗口截图中查找模板。

    Args:
        window_handle (int): 要查找的窗口句柄。
        _template: 模板图片，可以是图片路径或者 numpy.ndarray 数据。

    Returns:
        Optional[Tuple[float, float]]: 匹配区域的中心位置，如果未找到匹配则返回 None。
    """
    # 获取指定窗口的截图
    window_screenshot = __Screenshot_By_Window_Handle(window_handle)
    if window_screenshot is None:
        return None

    if isinstance(_template, str):
        # 如果传入的是图片路径，读取图片
        template = cv2.imread(_template, cv2.IMREAD_COLOR)
        if template is None:
            print("模板图像无法读取，请检查路径。")
            return None
    elif isinstance(_template, numpy.ndarray):
        # 如果传入的是 numpy.ndarray 数据，直接使用
        template = _template
    else:
        print("模板参数类型错误，请传入图片路径或 numpy.ndarray 数据。")
        return None

    return __Find_Template_Localtion(window_screenshot, template)


def _Check_Template_Exists_From_DC(_template) -> bool:
    """
    检查指定模板是否存在于指定窗口句柄对应的窗口画面中。

    Args:
        _template: 模板图片，可以是图片路径或者 numpy.ndarray 数据。
        _handle (int): 要检查的窗口句柄。

    Returns:
        bool: 如果模板存在于窗口画面中返回 True，否则返回 False。
    """
    # 获取指定窗口的截图
    window_screenshot = __Screenshot()
    if window_screenshot is None:
        return False

    if isinstance(_template, str):
        # 如果传入的是图片路径，读取图片
        template = cv2.imread(_template, cv2.IMREAD_COLOR)
        if template is None:
            print("模板图像无法读取，请检查路径。")
            return False
    elif isinstance(_template, numpy.ndarray):
        # 如果传入的是 numpy.ndarray 数据，直接使用
        template = _template
    else:
        print("模板参数类型错误，请传入图片路径或 numpy.ndarray 数据。")
        return False

    # 进行模板匹配
    result = __Find_Template_Localtion(window_screenshot, template)
    return result is not None


def _Check_Template_Exists_From_Handle(_template, _handle) -> bool:
    """
    检查指定模板是否存在于指定窗口句柄对应的窗口画面中。

    Args:
        _template: 模板图片，可以是图片路径或者 numpy.ndarray 数据。
        _handle (int): 要检查的窗口句柄。

    Returns:
        bool: 如果模板存在于窗口画面中返回 True，否则返回 False。
    """
    # 获取指定窗口的截图
    window_screenshot = __Screenshot_By_Window_Handle(_handle)
    if window_screenshot is None:
        return False

    if isinstance(_template, str):
        # 如果传入的是图片路径，读取图片
        template = cv2.imread(_template, cv2.IMREAD_COLOR)
        if template is None:
            print("模板图像无法读取，请检查路径。")
            return False
    elif isinstance(_template, numpy.ndarray):
        # 如果传入的是 numpy.ndarray 数据，直接使用
        template = _template
    else:
        print("模板参数类型错误，请传入图片路径或 numpy.ndarray 数据。")
        return False

    # 进行模板匹配
    result = __Find_Template_Localtion(window_screenshot, template)
    return result is not None


def _FindPic_Loop(_template,count=10):
    """循环查找,间隔1秒.

    Args:
        _template (_type_): 模板对象
        count (int, optional): 循环次数. Defaults to 10.

    Returns:
        _type_: 位置坐标
    """
    for _ in range(count):
        result = _FindPic_From_DC(_template)
        if result is not None:
            return result
        time.sleep(1)
    return None


def __load_image(image):
    if isinstance(image, str):
        img = cv2.imread(image, cv2.IMREAD_COLOR)
        if img is None:
            print("图像无法读取，请检查路径。")
        return img
    elif isinstance(image, numpy.ndarray):
        return image
    print("图像参数类型错误，请传入图片路径或 numpy.ndarray 数据。")
    return None

def _FindPic_Wait(_template, _wait_element, timeout=20):
    """等待某一个图像出现之后再开始找图。
    最后等待默认的20秒

    Args:
        _template: 模板图像，可以是图片路径或者 numpy.ndarray 数据。
        _wait_element: 需要先等待的图像，可以是图片路径或者 numpy.ndarray 数据。
        timeout (int, optional): 等待的时间。Defaults to 20。
    """
    start_time = time.time()
    wait_img = __load_image(_wait_element)
    if wait_img is None:
        return None
    template_img = __load_image(_template)
    if template_img is None:
        return None

    while time.time() - start_time < timeout:
        screen = __Screenshot()
        wait_result = __Find_Template_Localtion(screen, wait_img)
        if wait_result is not None:
            # 等待的图像出现，开始找模板图像
            template_result = __Find_Template_Localtion(screen, template_img)
            return template_result
        time.sleep(1)
    print("等待超时，未等到指定图像出现。")
    return None


def Moveto(point:tuple):
    pyautogui.moveTo(point[0],point[1])







def 验证(template_path, threshold=0.8):
    """
    进行模板匹配，并在原图上绘制匹配区域，最后显示结果。

    :param image_path: 大图（待搜索的图像）的文件路径
    :param template_path: 模板图的文件路径
    :param threshold: 匹配阈值，用于判断是否匹配成功，默认值为 0.8
    :return: None
    """
    # 读取大图和模板图
    image = __Screenshot()
    template = cv2.imread(template_path)

    if image is None or template is None:
        print("无法读取图像，请检查文件路径。")
        return

    # 获取模板图的高度和宽度
    h, w = template.shape[:2]

    # 执行模板匹配
    result = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)

    # 找到匹配值大于阈值的所有位置
    locations = numpy.where(result >= threshold)
    
    for loc in zip(*locations[::-1]):
        # 绘制矩形框标记匹配区域
        cv2.rectangle(image, loc, (loc[0] + w, loc[1] + h), (0, 255, 0), 2)
        print(loc)
    # 显示绘制后的图像
    cv2.imshow('Template Matching Result', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()





if __name__ == "__main__" :
    print("Program Start!")
    
    # 替换为你的模板图片路径
    
    point = _FindPic_Wait("./tasks/task_doudian/image/1.png","./tasks/task_doudian/image/0.png")
    print(point)
    Moveto(point=point)
    print("Program End!")
